#include ".\ourobject.h"

#include "stdafx.h"
#include "MemDC.h"
#include "Matrix3D.h"
#include "OurModel.h"
#include <iostream>
#include <math.h>
#include <assert.h>

OurObject::OurObject(OurModel* parent):
	m_parent(parent),m_nWireColor(RGB(0,0,0)),m_CenterOfMass(Matrix3D(1,4,true)),
	m_TransformedCenterOfMass(Matrix3D(1,4,true)),m_bBackFaceCullingOn(false),
	m_bNormalSwitchDirection(false),m_bColorPerVertex(false),
	m_bColorPerFace(false)
{
	m_CenterOfMass[0][3]=1;
	m_TransformedCenterOfMass[0][3]=1;
}

OurObject::~OurObject(void)
{
}

void OurObject::AddPoint(double dbX,double dbY,double dbZ)
{
	Matrix3D p(1,4);
	p[0][0] = dbX;
	p[0][1] = dbY;
	p[0][2] = dbZ;
	p[0][3] = 1;
	m_PointsVector.push_back(p);
	m_TransformedPoints.push_back(p);

	if(m_BoundingBoxPoints.size() == 0)
	{
		//initializing the bounding box
		for(int i=0;i<8;i++)
		{
			m_BoundingBoxPoints.push_back(p);
		}
	}
	if(m_AxesBounds.size() == 0)
	{
		//initializing object's axes
		for(int i=0;i<6;i++)
		{
			m_AxesBounds.push_back(p);
		}
	}
	
	m_CenterOfMass[0][0] = 
		((m_CenterOfMass[0][0] * m_PointsVector.size()) + dbX)/
		(m_PointsVector.size()+1);

	m_CenterOfMass[0][1] = 
		((m_CenterOfMass[0][1] * m_PointsVector.size()) + dbY)/
		(m_PointsVector.size()+1);
	
	m_CenterOfMass[0][2] = 
		((m_CenterOfMass[0][2] * m_PointsVector.size()) + dbZ)/
		(m_PointsVector.size()+1);


	m_AxesBounds[0][0][1] = m_CenterOfMass[0][1];
	m_AxesBounds[0][0][2] = m_CenterOfMass[0][2];

	m_AxesBounds[1][0][1] = m_CenterOfMass[0][1];
	m_AxesBounds[1][0][2] = m_CenterOfMass[0][2];


	m_AxesBounds[2][0][0] = m_CenterOfMass[0][0];
	m_AxesBounds[2][0][2] = m_CenterOfMass[0][2];

	m_AxesBounds[3][0][0] = m_CenterOfMass[0][0];
	m_AxesBounds[3][0][2] = m_CenterOfMass[0][2];

	m_AxesBounds[4][0][0] = m_CenterOfMass[0][0];
	m_AxesBounds[4][0][1] = m_CenterOfMass[0][1];

	m_AxesBounds[5][0][0] = m_CenterOfMass[0][0];
	m_AxesBounds[5][0][1] = m_CenterOfMass[0][1];

	if(dbX > m_BoundingBoxPoints[3][0][0])
	{
		m_AxesBounds[0][0][0] = dbX;
		for(unsigned i=0;i<4;i++)
		{
			m_BoundingBoxPoints[(2*i)+1][0][0]=dbX;
		}
	}
	if(dbX <  m_BoundingBoxPoints[2][0][0])
	{
		m_AxesBounds[1][0][0] = dbX;
		m_AxesBounds[1][0][1] = m_CenterOfMass[0][1];
		m_AxesBounds[1][0][2] = m_CenterOfMass[0][2];
		for(unsigned i=0;i<4;i++)
		{
				m_BoundingBoxPoints[2*i][0][0]=dbX;
		}
	}
	if(dbY > m_BoundingBoxPoints[3][0][1])
	{
		m_AxesBounds[2][0][0] = m_CenterOfMass[0][0];
		m_AxesBounds[2][0][1] = dbY;
		m_AxesBounds[2][0][2] = m_CenterOfMass[0][2];
		m_BoundingBoxPoints[2][0][1] = dbY;
		m_BoundingBoxPoints[3][0][1] = dbY;
		m_BoundingBoxPoints[6][0][1] = dbY;
		m_BoundingBoxPoints[7][0][1] = dbY;
	}
	if(dbY < m_BoundingBoxPoints[1][0][1])
	{
		m_AxesBounds[3][0][0] = m_CenterOfMass[0][0];
		m_AxesBounds[3][0][1] = dbY;
		m_AxesBounds[3][0][2] = m_CenterOfMass[0][2];
		m_BoundingBoxPoints[0][0][1] = dbY;
		m_BoundingBoxPoints[1][0][1] = dbY;
		m_BoundingBoxPoints[4][0][1] = dbY;
		m_BoundingBoxPoints[5][0][1] = dbY;
	}
	if(dbZ > m_BoundingBoxPoints[7][0][2])
	{
		m_AxesBounds[4][0][0] = m_CenterOfMass[0][0];
		m_AxesBounds[4][0][1] = m_CenterOfMass[0][1];
		m_AxesBounds[4][0][2] = dbZ;
		for(unsigned i=4;i<8;i++)
		{
			m_BoundingBoxPoints[i][0][2]=dbZ;
		}
	}
	if(dbZ < m_BoundingBoxPoints[1][0][2])
	{
		m_AxesBounds[5][0][0] = m_CenterOfMass[0][0];
		m_AxesBounds[5][0][1] = m_CenterOfMass[0][1];
		m_AxesBounds[5][0][2] = dbZ;
		for(unsigned i=0;i<4;i++)
		{
			m_BoundingBoxPoints[i][0][2]=dbZ;
		}
	}
	m_TransformedBoundingBoxPoints = m_BoundingBoxPoints;
	m_TransformedAxesBounds = m_AxesBounds;
	m_TransformedCenterOfMass = m_CenterOfMass;
}
bool OurObject::AddFace(vector<unsigned>& FacesIndex)
{
	for(unsigned i=0;i<FacesIndex.size();i++)
	{
		if(FacesIndex[i] >= m_PointsVector.size())
		{
			return false;
		}
	}
	m_FacesIndex.push_back(FacesIndex);
	m_FacesIndex.back().push_back(FacesIndex[0]);

	// -------------------------
	// filling FaceNormals stuff:
	
	if(FacesIndex.size()<3)
	{
		for(int i= FacesIndex.size();i<3;i++)
		{
			FacesIndex.push_back(FacesIndex[0]);
		}
	}
	Matrix3D line1 = m_PointsVector[FacesIndex[0]] - m_PointsVector[FacesIndex[1]];
	Matrix3D line2 = m_PointsVector[FacesIndex[1]] - m_PointsVector[FacesIndex[2]];

	// we compute the normal:
	Matrix3D normal = line1.CrossProduct(line2); 

	Matrix3D from(1,4,true);
	for(unsigned j=0;j<FacesIndex.size();j++)
		from += m_PointsVector[FacesIndex[j]];

	for (unsigned i(0) ; i < 3 ; ++i) // bah
		from[0][i] /= FacesIndex.size();
	from[0][3] = 1;

	// now we have "from" point pointing to the central of mass of our faceset.
	// let's get the "to" point by adding the normal coords to it.
	Matrix3D to(from);
	to += normal;
	to[0][3] = 1;

	m_FaceNormals.push_back(FaceNormal_t(from, to));
	m_TransformedFaceNormals.push_back(FaceNormal_t(from, to));
	// ----

	return true;
}


void OurObject::paint(CMemDC &memDC,bool bDrawBoundingBox,bool bDrawAxes,bool bDrawFaceNormals,bool bDrawVertexNormals)
{
	CPen pen(PS_SOLID,1,m_nWireColor);
	CPen *oldPen = memDC.SelectObject(&pen);
	
	for(unsigned i=0;i<m_FacesIndex.size();i++)
	{
		vector<unsigned>& Face = m_FacesIndex[i];
		
		if(IsBackFace(i) && m_bBackFaceCullingOn == true)
		{
			continue;
		}
		
		for(unsigned j=0;j<Face.size()-1;j++)
		{
			DrawLine(memDC,
					 m_TransformedPoints[Face[j]],
					 m_TransformedPoints[Face[j+1]]);
		}
	}
	if(bDrawBoundingBox == true)
	{
		PaintBoundingBox(memDC);
	}
	if(bDrawAxes == true)
	{
		PaintAxes(memDC);
	}
	if (bDrawFaceNormals) {
		PaintFaceNormals(memDC);
	}
	if (bDrawVertexNormals) {
		PaintVertexNormals(memDC);
	}

	//remove the new pan and restore old one - this will prevent resource leak
	memDC.SelectObject(oldPen);
}

void OurObject::DrawLine(CMemDC &memDC,
						 const Matrix3D& FromPoint,
						 const Matrix3D& ToPoint)
{
	memDC.MoveTo(static_cast<int>(FromPoint[0][0]),
				 static_cast<int>(FromPoint[0][1]));
	memDC.LineTo(static_cast<int>(ToPoint[0][0]),
				 static_cast<int>(ToPoint[0][1]));
}

void OurObject::MultiplyWithMatrix(const Matrix3D& m)
{
	for(unsigned i=0;i<m_PointsVector.size();i++)
	{
		m_TransformedPoints[i] = m_TransformedPoints[i] * m ;
		for(unsigned j=0;j<3;j++)
		{
			m_TransformedPoints[i][0][j] /= m_TransformedPoints[i][0][3];
		}
		m_TransformedPoints[i][0][3] = 1;
	}
	for(unsigned i=0;i<8;i++)
	{
		m_TransformedBoundingBoxPoints[i] = 
			m_TransformedBoundingBoxPoints[i] * m;
		for(unsigned j=0;j<3;j++)
		{
			m_TransformedBoundingBoxPoints[i][0][j] /= m_TransformedBoundingBoxPoints[i][0][3];
		}
		m_TransformedBoundingBoxPoints[i][0][3] = 1;
	}

	m_TransformedCenterOfMass = m_TransformedCenterOfMass *m;
	for(unsigned j=0;j<3;j++)
	{
		m_TransformedCenterOfMass[0][j] /= m_TransformedCenterOfMass[0][3];
	}
	m_TransformedCenterOfMass[0][3] = 1;

	for(unsigned i=0;i<6;i++)
	{
		m_TransformedAxesBounds[i] = 
			m_TransformedAxesBounds[i] * m;
		for(unsigned j=0;j<3;j++)
		{
			m_TransformedAxesBounds[i][0][j] /= m_TransformedAxesBounds[i][0][3];
		}
		m_TransformedAxesBounds[i][0][3] = 1;
	}

	for (unsigned i(0);i<m_TransformedFaceNormals.size();++i) {
		m_TransformedFaceNormals[i].first = m_TransformedFaceNormals[i].first * m;
		m_TransformedFaceNormals[i].second = m_TransformedFaceNormals[i].second * m;
		for(unsigned j=0;j<3;j++) {
			m_TransformedFaceNormals[i].first[0][j] /= m_TransformedFaceNormals[i].first[0][3];
			m_TransformedFaceNormals[i].second[0][j] /= m_TransformedFaceNormals[i].second[0][3];
		}
		m_TransformedFaceNormals[i].first[0][3] = 1;
		m_TransformedFaceNormals[i].second[0][3] = 1;

		// now we would like to make it a normal, aka size = 1 (or maybe larger, so we can actually see)
		Matrix3D vector = m_TransformedFaceNormals[i].second - m_TransformedFaceNormals[i].first;
		double vectorsize = sqrt( pow(vector[0][0],2) + pow(vector[0][1],2) + pow(vector[0][2],2) ) / ms_NormalSize;
		vector[0][0] /= vectorsize ;
		vector[0][1] /= vectorsize ;
		vector[0][2] /= vectorsize ;
	
		m_TransformedFaceNormals[i].second = m_TransformedFaceNormals[i].first;
		m_TransformedFaceNormals[i].second += vector;
		m_TransformedFaceNormals[i].second[0][3] = 1;
	}

	// vertex normals crap:
	for (unsigned i(0);i<m_TransformedVertexNormals.size();++i) {
		m_TransformedVertexNormals[i].second = m_TransformedVertexNormals[i].second * m;
		for(unsigned j=0;j<3;j++)
			m_TransformedVertexNormals[i].second[0][j] /= m_TransformedVertexNormals[i].second[0][3];
		m_TransformedVertexNormals[i].second[0][3] = 1;

		// now we would like to make it a normal, aka size = 1 (or maybe larger, so we can actually see)
		Matrix3D vector = m_TransformedVertexNormals[i].second - m_TransformedPoints[m_TransformedVertexNormals[i].first];
		double vectorsize = sqrt( pow(vector[0][0],2) + pow(vector[0][1],2) + pow(vector[0][2],2) ) / ms_NormalSize;
		vector[0][0] /= vectorsize ;
		vector[0][1] /= vectorsize ;
		vector[0][2] /= vectorsize ;
	
		m_TransformedVertexNormals[i].second = m_TransformedPoints[m_TransformedVertexNormals[i].first];
		m_TransformedVertexNormals[i].second += vector;
		m_TransformedVertexNormals[i].second[0][3] = 1;
	}
}

void OurObject::reset()
{
	m_TransformedPoints = m_PointsVector;
	m_TransformedBoundingBoxPoints = m_BoundingBoxPoints;
	m_TransformedCenterOfMass = m_CenterOfMass;
	m_TransformedAxesBounds = m_AxesBounds;
	m_TransformedFaceNormals = m_FaceNormals;
	m_TransformedVertexNormals = m_VertexNormals;
}


void OurObject::PaintBoundingBox(CMemDC &memDC)
{
	DrawLine(memDC,m_TransformedBoundingBoxPoints[0],m_TransformedBoundingBoxPoints[1]);
	DrawLine(memDC,m_TransformedBoundingBoxPoints[0],m_TransformedBoundingBoxPoints[2]);
	DrawLine(memDC,m_TransformedBoundingBoxPoints[0],m_TransformedBoundingBoxPoints[4]);
	DrawLine(memDC,m_TransformedBoundingBoxPoints[5],m_TransformedBoundingBoxPoints[4]);
	DrawLine(memDC,m_TransformedBoundingBoxPoints[5],m_TransformedBoundingBoxPoints[1]);
	DrawLine(memDC,m_TransformedBoundingBoxPoints[5],m_TransformedBoundingBoxPoints[7]);
	DrawLine(memDC,m_TransformedBoundingBoxPoints[6],m_TransformedBoundingBoxPoints[2]);
	DrawLine(memDC,m_TransformedBoundingBoxPoints[6],m_TransformedBoundingBoxPoints[4]);
	DrawLine(memDC,m_TransformedBoundingBoxPoints[6],m_TransformedBoundingBoxPoints[7]);
	DrawLine(memDC,m_TransformedBoundingBoxPoints[3],m_TransformedBoundingBoxPoints[2]);
	DrawLine(memDC,m_TransformedBoundingBoxPoints[3],m_TransformedBoundingBoxPoints[7]);
	DrawLine(memDC,m_TransformedBoundingBoxPoints[3],m_TransformedBoundingBoxPoints[1]);
}

void OurObject::PaintAxes(CMemDC &memDC)
{
	CPen pen(PS_SOLID,3,m_nWireColor);
	CPen *oldPen = memDC.SelectObject(&pen);

	//Center-left
	//Center-right
	//Center-top
	//Center-down

	for(int i=0;i<6;i++)
	{
		memDC.MoveTo(static_cast<int>(m_TransformedCenterOfMass[0][0]),
						 static_cast<int>(m_TransformedCenterOfMass[0][1]));
		memDC.LineTo(static_cast<int>(m_TransformedAxesBounds[i][0][0]),
						 static_cast<int>(m_TransformedAxesBounds[i][0][1]));
	}

	memDC.SelectObject(oldPen);
}

void OurObject::SetColor(int nWireColor)
{
	m_nWireColor = nWireColor;
}

void OurObject::ComputeFaceNormals (void) {
	// making an assumption all our faces are at least 3 points long, and on same
	// plane (this is always the case with triangles)

	static Matrix3D from(1,4);
	m_FaceNormals.clear();

	for(unsigned i=0;i<m_FacesIndex.size();i++)
	{
		vector<unsigned>& Face = m_FacesIndex[i];

		Matrix3D line1 = m_PointsVector[Face[1]] - m_PointsVector[Face[0]];
		Matrix3D line2 = m_PointsVector[Face[2]] - m_PointsVector[Face[1]];

		// we compute the normal:
		Matrix3D normal = line1.CrossProduct(line2); 

		from[0][0] = from[0][1] = from[0][2] = from[0][3] = 0; // ugly. [dont even need homo coord]
		for(unsigned j=0;j<Face.size();j++)
			from += m_PointsVector[Face[j]];

		for (unsigned i(0) ; i < 3 ; ++i) // bah
			from[0][i] /= Face.size();
		from[0][3] = 1;

		// now we have "from" point pointing to the central of mass of our faceset.
		// let's get the "to" point by adding the normal coords to it.
		Matrix3D to(from);
		to += normal;
		to[0][3] = 1;

		m_FaceNormals.push_back(FaceNormal_t(from, to));
	}
}

void OurObject::PaintFaceNormals (CMemDC &memDC) {
	CPen pen(PS_SOLID,1,RGB(255,0,0));
	CPen *oldPen = memDC.SelectObject(&pen);

	for (unsigned i(0);i<m_TransformedFaceNormals.size();++i)
		DrawLine(memDC, m_TransformedFaceNormals[i].first, m_TransformedFaceNormals[i].second);

	memDC.SelectObject(oldPen);
}

void OurObject::AddVertexNormal (unsigned vertex, double x, double y, double z) 
{
	if(m_VertexNormals.size() == 0)
	{
		m_VertexNormals.resize(m_PointsVector.size(),pair<unsigned,Matrix3D>(0,Matrix3D(0,0,true)));
		m_TransformedVertexNormals.resize(m_PointsVector.size(),pair<unsigned,Matrix3D>(0,Matrix3D(0,0,true)));
	}
	Matrix3D normal(1,4);
	normal[0][0] = x;
	normal[0][1] = y;
	normal[0][2] = z;
	normal[0][3] = 1;
	pair<unsigned,Matrix3D> p(vertex, normal);
	m_VertexNormals[vertex] = p;
	m_TransformedVertexNormals[vertex] = p;
	//m_VertexNormals.push_back(p);
	//m_TransformedVertexNormals.push_back(p);
}

void OurObject::PaintVertexNormals (CMemDC &memDC) {
	CPen pen(PS_SOLID,1,RGB(0,0,255));
	CPen *oldPen = memDC.SelectObject(&pen);

	for (unsigned i(0);i<m_TransformedVertexNormals.size();++i)
		DrawLine(memDC, m_TransformedPoints[m_TransformedVertexNormals[i].first], 
				 m_TransformedVertexNormals[i].second);

	memDC.SelectObject(oldPen);
}

void OurObject::ModifyFaceNormal (unsigned face_idx, double x, double y, double z) 
{

	// only change such that the "to" will be "from" + x,y,z

	// (from is first, to is second)

	if (face_idx >= m_FaceNormals.size())

		return;

	m_FaceNormals[face_idx].second[0][0] = m_FaceNormals[face_idx].first[0][0] + x;

	m_FaceNormals[face_idx].second[0][1] = m_FaceNormals[face_idx].first[0][1] + y;

	m_FaceNormals[face_idx].second[0][2] = m_FaceNormals[face_idx].first[0][2] + z;

	m_TransformedFaceNormals[face_idx] = m_FaceNormals[face_idx];

}


void OurObject::AddFaces(
			multiset<pair<pair<OurObject*,unsigned>,vector<Matrix3D*> >,
			OurModel::FacesetYComparator>& Faces,
			int& nScreenYmin,
			int& nScreenYmax,
			int& nScreenXmin,
			int& nScreenXmax,
			double& dbScreenZMax,
			double& dbScreenZMin)
{
	
	for(unsigned i=0;i<m_FacesIndex.size();i++)
	{
		if(IsBackFace(i) && m_bBackFaceCullingOn)
		{
			continue;
		}
		vector<Matrix3D*> Face;
		for(unsigned j=0;j<m_FacesIndex[i].size();j++)
		{
			Face.push_back(&m_TransformedPoints[m_FacesIndex[i][j]]);
			Matrix3D& p = m_TransformedPoints[m_FacesIndex[i][j]];
			
			nScreenYmax = max(nScreenYmax,p[0][1]);
			nScreenYmin = min(nScreenYmin,p[0][1]);
			nScreenXmax = max(nScreenXmax,p[0][0]);
			nScreenXmin = min(nScreenXmin,p[0][0]);

			dbScreenZMax = max(dbScreenZMax,p[0][2]);
			dbScreenZMin = min(dbScreenZMin,p[0][2]);
		}
		pair<OurObject*,unsigned> back(this,i);
		Faces.insert(
			pair<pair<OurObject*,unsigned>,vector<Matrix3D*> >(back,Face));
	}
}


int OurObject::GetMinY()
{
	double dbResult = m_TransformedBoundingBoxPoints[0][0][1];
	for(unsigned i=1;i<m_TransformedBoundingBoxPoints.size();i++)
	{
		dbResult = min(dbResult,m_TransformedBoundingBoxPoints[i][0][1]);
	}
	return Matrix3D::roundInt(dbResult);
}
int OurObject::GetMaxY()
{
	double dbResult = m_TransformedBoundingBoxPoints[0][0][1];
	for(unsigned i=1;i<m_TransformedBoundingBoxPoints.size();i++)
	{
		dbResult = max(dbResult,m_TransformedBoundingBoxPoints[i][0][1]);
	}
	return Matrix3D::roundInt(dbResult);
}
int OurObject::GetMinX()
{
	int dbResult = m_TransformedBoundingBoxPoints[0][0][0];
	for(unsigned i=1;i<m_TransformedBoundingBoxPoints.size();i++)
	{
		dbResult = min(dbResult,m_TransformedBoundingBoxPoints[i][0][0]);
	}
	return Matrix3D::roundInt(dbResult);
}
int OurObject::GetMaxX()
{
	int dbResult = m_TransformedBoundingBoxPoints[0][0][0];
	for(unsigned i=1;i<m_TransformedBoundingBoxPoints.size();i++)
	{
		dbResult = max(dbResult,m_TransformedBoundingBoxPoints[i][0][0]);
	}
	return Matrix3D::roundInt(dbResult);
}

double OurObject::GetMaxZ()
{
	double dbResult = m_TransformedBoundingBoxPoints[0][0][2];
	for(unsigned i=1;i<m_TransformedBoundingBoxPoints.size();i++)
	{
		dbResult = max(dbResult,m_TransformedBoundingBoxPoints[i][0][2]);
	}
	return dbResult;
}

double OurObject::GetMinZ()
{
	double dbResult = m_TransformedBoundingBoxPoints[0][0][2];
	for(unsigned i=1;i<m_TransformedBoundingBoxPoints.size();i++)
	{
		dbResult = min(dbResult,m_TransformedBoundingBoxPoints[i][0][2]);
	}
	return dbResult;
}

void OurObject::BackFaceCulling(bool bIsOn,bool bNormalSwitchDirection)
{
	m_bBackFaceCullingOn = bIsOn;
	m_bNormalSwitchDirection = bNormalSwitchDirection;
}

void OurObject::CalculateVertexNormals()
{
	//the normals are calculated from the normal average of the neighboring faces
	
	//initialized the normals to be 0
	for(unsigned i=0;i<m_PointsVector.size();i++)
	{
		Matrix3D normal(1,4,true);
		pair<unsigned,Matrix3D> p(i, normal);
		
		m_VertexNormals.push_back(p);
		
	}
	//fixing the normals
	for(unsigned i=0;i<m_FacesIndex.size();i++)
	{
		for(unsigned j=0;j<m_FacesIndex[i].size();j++)
		{
			pair<unsigned,Matrix3D>& p = 
				m_VertexNormals[m_FacesIndex[i][j]];
			/*p.second[0][0] +=  
				m_FaceNormals[i].second[0][0] - m_FaceNormals[i].first[0][0];
			p.second[0][1] +=
				m_FaceNormals[i].second[0][1] - m_FaceNormals[i].first[0][1];
			p.second[0][2] +=
				m_FaceNormals[i].second[0][2] - m_FaceNormals[i].first[0][2];*/
			p.second = m_FaceNormals[i].second-m_FaceNormals[i].first;
			p.second[0][3] = 1;
		}
	}

	//transforming the normals to be of size 1

	for(unsigned i=0;i<m_VertexNormals.size();i++)
	{
		Matrix3D& BasePoint = 
			m_PointsVector[m_VertexNormals[i].first];
		double dbX = m_VertexNormals[i].second[0][0];
		double dbY = m_VertexNormals[i].second[0][1];
		double dbZ = m_VertexNormals[i].second[0][2];
		double dbNormalSize = sqrt(pow(dbX,2)+pow(dbY,2)+pow(dbZ,2));
		m_VertexNormals[i].second[0][0] = 
			(dbX/dbNormalSize) + BasePoint[0][0];
		m_VertexNormals[i].second[0][1] = 
			(dbY/dbNormalSize) + BasePoint[0][1];
		m_VertexNormals[i].second[0][2] = 
			(dbZ/dbNormalSize) + BasePoint[0][2];
	}
	m_TransformedVertexNormals.clear();
	for(unsigned i=0;i<m_VertexNormals.size();i++)
	{
		m_TransformedVertexNormals.push_back(m_VertexNormals[i]);
	}
}

void OurObject::AddTriangulates()
{
	for(unsigned i=0;i<m_FacesIndex.size();i++)
	{
		for(int j=1;j<m_FacesIndex[i].size()-2;j++)
		{
			vector<unsigned> triangle;
			triangle.push_back(m_FacesIndex[i][0]);
			triangle.push_back(m_FacesIndex[i][j]);
			triangle.push_back(m_FacesIndex[i][j+1]);
			triangle.push_back(m_FacesIndex[i][0]);
			m_TriangulatedFacesIndex.push_back(triangle);
			//updates the vector that maps the triangles to their original face normal
			m_TriangulatedFaceNormals.push_back(i);
		}
		
	
	}
}


void OurObject::SetKa(vector<int>& Ka)
{
	assert(Ka.size()==3);
	m_Ka[0][0] = Ka[0];
	m_Ka[1][1] = Ka[0];
	m_Ka[2][2] = Ka[0];
}
void OurObject::SetKd(vector<int>& Kd)
{
	assert(Kd.size()==3);
	m_Kd[0][0] = Kd[0];
	m_Kd[1][1] = Kd[1];
	m_Kd[2][2] = Kd[2];
}
void OurObject::SetKs(vector<int>& Ks)
{
	assert(Ks.size()==3);
	m_Ks[0][0] = Ks[0];
	m_Ks[1][1] = Ks[1];
	m_Ks[2][2] = Ks[2];
}
void OurObject::SetN(int nN)
{
	m_nN = nN;
}

vector<Matrix3D> OurObject::GetColorOfVertices(unsigned FaceIndex)
{
	vector<Matrix3D> Colors;
	//vector<unsigned>& face = m_TriangulatedFacesIndex[FaceIndex];
	vector<unsigned>& face = m_FacesIndex[FaceIndex];
	if(IsColorPerVertex())
	{
		for(unsigned i=0;i<face.size();i++)
		{
			Colors.push_back(m_VertexColors[face[i]]);
		}
		return Colors;
	}
	if(IsColorPerFace())
	{
		for(unsigned i=0;i<face.size();i++)
		{
			//Colors.push_back(m_FaceColors[m_TriangulatedFaceNormals[FaceIndex]]);
			Colors.push_back(m_FaceColors[FaceIndex]);
		}
		return Colors;
	}
	for(unsigned i=0;i<face.size();i++)
	{
		Colors.push_back(m_Kd);
	}
	return Colors;
}

Matrix3D OurObject::GetFaceColor(unsigned index)
{
	if(IsColorPerVertex())
	{
		//return the first vertex color
		//vector<unsigned>& face = m_TriangulatedFacesIndex[index];
		vector<unsigned>& face = m_FacesIndex[index];
		return m_VertexColors[face[0]];
	}
	else if(IsColorPerFace())
	{
		//return m_FaceColors[m_TriangulatedFaceNormals[index]]; 
		return m_FaceColors[index]; 
	}
	else
	{
		return m_Kd;
	}
}


void OurObject::SetVertexColor(unsigned index, int nR, int nG, int nB)
{
	assert(index < m_PointsVector.size());
	if(m_VertexColors.size() == 0)
	{
		for(unsigned i=0;i<m_PointsVector.size();i++)
		{
			m_VertexColors.push_back(Matrix3D(4,4,false));
		}
	}
	if(index == 829)
	{
		cout<<"break";
	}
	Matrix3D Color;
	Color[0][0] = nR;
	Color[1][1] = nG;
	Color[2][2] = nB;

	m_VertexColors[index] = Color;
}

void OurObject::SetFaceColor(unsigned index, int nR, int nG, int nB)
{
	assert(index < m_FacesIndex.size());
	if(m_FaceColors.size() == 0)
	{
		for(unsigned i=0;i<m_FacesIndex.size();i++)
		{
			m_FaceColors.push_back(Matrix3D(4,4,false));
		}
	}

	Matrix3D Color;
	Color[0][0] = nR;
	Color[1][1] = nG;
	Color[2][2] = nB;

	m_FaceColors[index] = Color;
}

void OurObject::ColorPerVertex(bool bIsTrue)
{
	m_bColorPerVertex = bIsTrue;
}

void OurObject::ColorPerFace(bool bIsTrue)
{
	m_bColorPerFace = true;
}

bool OurObject::IsColorPerVertex()
{
	return m_bColorPerVertex;
}

bool OurObject::IsColorPerFace()
{
	return m_bColorPerFace;
}

Matrix3D OurObject::GetKa(unsigned PolygonIndex)
{
	return GetFaceColor(PolygonIndex)*m_AmbientIntensity;
	//return m_Ka;
}

Matrix3D OurObject::GetKd(unsigned PolygonIndex)
{
	return GetFaceColor(PolygonIndex);
}

Matrix3D OurObject::GetKs()
{
	return m_Ks;
}

int OurObject::GetN()
{
	return m_nN;
}

Matrix3D OurObject::GetPolygonNormal(unsigned index)
{
	//assert(index < m_TriangulatedFacesIndex.size());
	assert(index < m_FacesIndex.size());
	Matrix3D Result(1,4,true);
	Result = m_TransformedFaceNormals[index].second - 
			 m_TransformedFaceNormals[index].first;
	
	if(m_bNormalSwitchDirection == true)
	{
		Result *= (-1);
	}
	
	return Result;
}

Matrix3D OurObject::GetVertexNormal(unsigned index)
{
	assert(index < m_PointsVector.size());
	Matrix3D Result(1,4,true);
	Result = m_TransformedVertexNormals[index].second - 
		m_TransformedPoints[m_TransformedVertexNormals[index].first];
	
	/*Result = m_TransformedFaceNormals[index].second - 
		m_TransformedFaceNormals[index].first;*/
	Result.Normalize(1.0);
	if(m_bNormalSwitchDirection == true)
	{
		Result *= (-1);
	}
	return Result;
}

void OurObject::SetAmbientIntensity(double dbAmbientIntensity)
{
	m_AmbientIntensity[0][0] = dbAmbientIntensity;
	m_AmbientIntensity[1][1] = dbAmbientIntensity;
	m_AmbientIntensity[2][2] = dbAmbientIntensity;
}
Matrix3D& OurObject::GetAmbientIntensity()
{
	return m_AmbientIntensity;
}

bool OurObject::IsBackFace(unsigned index)
{
	Matrix3D normal = GetPolygonNormal(index);
	if(normal[0][2]>=0)
	{
		return true;
	}
	return false;
}

void OurObject::SetFlatColorOfFaces(
									 vector<pair<Matrix3D,bool> >& LightSources,
									 vector<Matrix3D>& LightSourcesIntensity,
									 Matrix3D& AmbientIntensity,
									 Matrix3D& ViewVector,
									 double AmbientFactor,
									 double DiffuseFactor,
									 double SpecularFactor,
									 int nCosineFactor)
{
	Matrix3D EmptyColor(0,0,true);
	m_FlatFaceColor.clear();
	m_FlatFaceColor.resize(m_FacesIndex.size(),EmptyColor);

	for(unsigned i=0;i<m_FacesIndex.size();i++)
	{
		m_FlatFaceColor[i] = 
			CalculateColor( 
			CalculateCenterOfFace(m_FacesIndex[i]),
			GetKa(i),
			GetKd(i),
			GetKs(),
			ViewVector,
			LightSources,
			GetPolygonNormal(i),
			AmbientIntensity,
			LightSourcesIntensity,
			GetN(),
			AmbientFactor,
			DiffuseFactor,
			SpecularFactor,
			nCosineFactor);
	}
}

void OurObject::SetGouraudColorsForVertices(
				vector<pair<Matrix3D,bool> >& LightSources,
				vector<Matrix3D>& LightSourcesIntensity,
				Matrix3D& AmbientIntensity,
				Matrix3D& ViewVector,
				double AmbientFactor,
				double DiffuseFactor,
				double SpecularFactor,
				int nCosineFactor)
{
	Matrix3D EmptyColor(0,0,true);
	m_GouraudVertexColor.clear();
	m_GouraudVertexColor.resize(m_PointsVector.size(),EmptyColor);

	for(unsigned i=0;i<m_FacesIndex.size();i++)
	{
		for(unsigned j=0;j<m_FacesIndex[i].size();j++)
		{
			if(m_GouraudVertexColor[m_FacesIndex[i][j]].IsEmpty() == false)
			{
				continue;
			}
			if(m_TransformedPoints[m_FacesIndex[i][j]][0][0] <= 459 &&
				m_TransformedPoints[m_FacesIndex[i][j]][0][0] >= 455 &&
				m_TransformedPoints[m_FacesIndex[i][j]][0][1] <= 256 &&
				m_TransformedPoints[m_FacesIndex[i][j]][0][1] >= 252)
			{
				Matrix3D n = GetVertexNormal(m_FacesIndex[i][j]);
				double dbZ = n[0][2];
				cout<<"kaka"<<endl;
				/*m_TransformedPoints[m_TransformedVertexNormals[i].first], 
					m_TransformedVertexNormals[i].second*/
			}
			m_GouraudVertexColor[m_FacesIndex[i][j]] = 
				CalculateColor( m_TransformedPoints[m_FacesIndex[i][j]],
								GetKa(i),
								GetKd(i),
								GetKs(),
								ViewVector,
								LightSources,
								GetVertexNormal(m_FacesIndex[i][j]),
								AmbientIntensity,
								LightSourcesIntensity,
								GetN(),
								AmbientFactor,
								DiffuseFactor,
								SpecularFactor,
								nCosineFactor);
		}
	}
}

Matrix3D OurObject::GetFlatColor(unsigned FaceIndex)
{
	assert(FaceIndex < m_FlatFaceColor.size());
	return m_FlatFaceColor[FaceIndex];
}

Matrix3D OurObject::GetGouraudColor(unsigned VertexIndex)
{
	assert(VertexIndex < m_GouraudVertexColor.size());
	return m_GouraudVertexColor[VertexIndex];
}

Matrix3D OurObject::CalculateColor( 
								  Matrix3D& CenterOfFace,
								  Matrix3D& Ka,
								  Matrix3D& Kd,
								  Matrix3D& Ks,
								  Matrix3D& ViewVector,
								  vector<pair<Matrix3D,bool> >& LightVectors,
								  Matrix3D& SurfaceNormal,
								  Matrix3D& AmbientIntensity,
								  vector<Matrix3D>& LightSourcesIntensity,
								  double nShininess,
								  double dbAmbientFactor,
								  double dbDiffuseFactor,
								  double dbSpecularFactor,
								  double nCosineFactor)
{
	int nR;
	int nG;
	int nB;

	//the ambient part


	//Matrix3D Result = Ka*AmbientIntensity;
	Matrix3D Result = Ka*AmbientIntensity*(dbAmbientFactor);

	assert(LightVectors.size() == LightSourcesIntensity.size());

	Matrix3D DiffuseSum(4,4,true);
	Matrix3D ReflectionSum(4,4,true);
	for(unsigned l=0;l<LightVectors.size();l++)
	{
		Matrix3D LightVector(1,4,true);
		if(LightVectors[l].second == true)
		{
			//is point source, we should calculate the direction
			LightVector = CenterOfFace-LightVectors[l].first;
			LightVector.Normalize(1.0);
		}
		else
		{
			LightVector = LightVectors[l].first;
		}
		//the diffuse part
		double NL = 0;
		for(unsigned i=0;i<3;i++)
		{
			NL+= SurfaceNormal[0][i] * (-LightVector[0][i]);
		}
		if(NL<0)
		{
			NL=0;
			continue;
		}
		else
		{
			cout<<"yippie";
		}

		DiffuseSum += (LightSourcesIntensity[l]*NL);

		//the reflection part
		double RV = 0;
		Matrix3D ReflectionVector = ((SurfaceNormal*(2*NL))+LightVector);
		//Matrix3D ReflectionVector = LightVectors[l];
		//ReflectionVector+= ((SurfaceNormal - LightVectors[l])*2);
		//ReflectionVector.Normalize(1.0);
		for(unsigned i=0;i<3;i++)
		{
			RV += ReflectionVector[0][i]*(ViewVector[0][i]*(-1.0));
		}
		if(RV<0)
		{
			RV=0;
			continue;
		}
		RV = pow(RV,nShininess*nCosineFactor);
		ReflectionSum += (LightSourcesIntensity[l]*RV);
	}
	Result += Kd*DiffuseSum*(dbDiffuseFactor);
	Result += Ks*ReflectionSum*(dbSpecularFactor);

	if(Result[0][0] < 0 || Result[1][1] < 0 || Result[2][2] < 0)
	{
		Result = Matrix3D(4,4,true);
	}

	return Result;
}

Matrix3D OurObject::CalculateCenterOfFace(vector<unsigned>& Face)
{
	Matrix3D Center(1,4,true);
	for(unsigned i=0;i<Face.size();i++)
	{
		Center += m_TransformedPoints[Face[i]];
	}
	Center *= (1.0/Face.size());
	return Center;
}

unsigned OurObject::GetVertexIndex(unsigned FaceIndex,unsigned indexInFace)
{
	assert(FaceIndex < m_FacesIndex.size());
	assert(indexInFace < m_FacesIndex[FaceIndex].size());
	return m_FacesIndex[FaceIndex][indexInFace];
}